
package com.eastfair.venueservice.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.eastfair.boot.request.PageParams;
import com.eastfair.builder.dto.ExhibitorDTO;
import com.eastfair.constant.BusinessConstant;
import com.eastfair.core.base.R;
import com.eastfair.core.exception.BizException;
import com.eastfair.home.api.HomeHomeBondManageFeign;
import com.eastfair.home.dto.ReportSpaceAuditDTO;
import com.eastfair.projectcore.enumeration.MenuAffiliationEnum;
import com.eastfair.venueservice.dto.BondManageDTO;
import com.eastfair.venueservice.dto.ExhibitionSettlementDTO;
import com.eastfair.venueservice.entity.BondManage;
import com.eastfair.venueservice.entity.ServiceProvider;
import com.eastfair.venueservice.enumeration.BondManageOrderStatusEnum;
import com.eastfair.venueservice.enums.BondManagePayStatusEnum;
import com.eastfair.venueservice.exceptioncode.VenueServiceExceptionCode;
import com.eastfair.venueservice.service.*;
import com.eastfair.venueservice.vo.BondManageVO;
import com.eastfair.venueservice.vo.ExhibitionSettlementVO;
import com.eastfair.venueservice.vo.ServiceProviderVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * 主场保证金 service.
 *
 * @author dqq
 * @date 2023 /4/18
 */
@Service
@Slf4j
public class HomeBondManageServiceImpl implements HomeBondManageService {

    @Resource
    private BondManageService bondManageService;

    @Resource
    private ServiceProviderService serviceProviderService;

    @Resource
    private ServiceProviderSpaceService serviceProviderSpaceService;

    @Resource
    private ExhibitionServiceProviderService exhibitionServiceProviderService;

    @Resource
    private ViolationFineService violationFineService;

    @Resource
    private VenueManageExhibitorService venueManageExhibitorService;

    @Resource
    private VenueManageTransactionOrderService venueManageTransactionOrderService;

    @Resource
    private HomeHomeBondManageFeign homeHomeBondManageFeign;

    @Resource(name = "threadPoolTaskExecutor")
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;

    @Resource
    private TransactionTemplate transactionTemplate;

    private static QueryWrapper<BondManage> createBondManageVOQueryWrapper(BondManageDTO model) {
        QueryWrapper<BondManage> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda()
                .eq(BondManage::getIsDeleted, BusinessConstant.DELETE_NO)
                .eq(BondManage::getBelongSystem, MenuAffiliationEnum.P_COMPOSITE_PRODUCER.getCode())
                .eq(BondManage::getSystemScene, MenuAffiliationEnum.P_COMPOSITE_COURT.getCode())
                .eq(Objects.nonNull(model.getExhibitionManageId()), BondManage::getExhibitionManageId, model.getExhibitionManageId())
                .eq(Objects.nonNull(model.getServiceProviderId()), BondManage::getServiceProviderId, model.getServiceProviderId())
                .eq(StrUtil.isNotBlank(model.getSpaceCode()), BondManage::getSpaceCode, model.getSpaceCode())
                .eq(StrUtil.isNotBlank(model.getSiteCode()), BondManage::getSiteCode, model.getSiteCode())
                .like(StrUtil.isNotBlank(model.getOrderNumber()), BondManage::getOrderNumber, model.getOrderNumber())
                .eq(model.getOrderStatus() != null, BondManage::getOrderStatus, model.getOrderStatus())
                .eq(StrUtil.isNotBlank(model.getPayStatus()), BondManage::getPayStatus, model.getPayStatus())
                .in(model.getSpaceCodeList() != null && !model.getSpaceCodeList().isEmpty(), BondManage::getSpaceCode, model.getSpaceCodeList())
                .orderByDesc(BondManage::getCreateTime);
        return queryWrapper;
    }

    @Override
    public Page<BondManageVO> listBondManageVOs(PageParams<BondManageDTO> pageParams) {
        log.info("listBondManageVOs - 分页查询保证金订单, pageParams={}", pageParams);
        Page<BondManage> pageParam = pageParams.buildPage();
        BondManageDTO model = pageParams.getModel();
        if (model.getHomeId() != null) {
            // 查询主场在展会中负责的展厅
            List<String> spaceCodeList = serviceProviderSpaceService.querySpaceCodeListByExhibitionId(model.getExhibitionManageId(), model.getHomeId());
            model.setSpaceCodeList(spaceCodeList);
        }
        QueryWrapper<BondManage> queryWrapper = createBondManageVOQueryWrapper(model);
        Page<BondManage> page = bondManageService.page(pageParam, queryWrapper);
        Page<BondManageVO> voPage = new Page<>();
        BeanUtil.copyProperties(page, voPage);
        List<BondManage> list = page.getRecords();
        if (list == null || list.isEmpty()) {
            return voPage;
        }
        List<BondManageVO> voList = list
                .stream()
                .map(bondManage -> BeanUtil.toBean(bondManage, BondManageVO.class))
                .collect(Collectors.toList());
        voPage.setRecords(voList);
        return voPage;
    }

    @Override
    public List<BondManage> listByExhibitorId(Long exhibitorId) {
        BondManageDTO query = new BondManageDTO();
        query.setExhibitorId(exhibitorId);
        query.setBelongSystem(MenuAffiliationEnum.P_COMPOSITE_PRODUCER.getCode());
        query.setSystemScene(MenuAffiliationEnum.P_COMPOSITE_COURT.getCode());
        return bondManageService.listBondManage(query);
    }

    @Override
    public List<BondManageVO> listVOByExhibitorIdList(List<Long> exhibitorIdList) {
        log.info("listVOByExhibitorIdList - 展商ID查询保证金订单, exhibitorIdList={}", exhibitorIdList);
        BondManageDTO query = new BondManageDTO();
        query.setExhibitorIdList(exhibitorIdList);
        query.setBelongSystem(MenuAffiliationEnum.P_COMPOSITE_PRODUCER.getCode());
        query.setSystemScene(MenuAffiliationEnum.P_COMPOSITE_COURT.getCode());
        List<BondManage> list = bondManageService.listBondManage(query);
        if (list == null || list.isEmpty()) {
            return Collections.emptyList();
        }
        return list
                .stream()
                .map(bondManage -> BeanUtil.toBean(bondManage, BondManageVO.class))
                .collect(Collectors.toList());
    }

    @Override
    public ExhibitionSettlementVO getHomeBondManageSettlement(ExhibitionSettlementDTO query) {
        // 查询场馆设置的保证金
        BondManageDTO bondManageQuery = new BondManageDTO();
        bondManageQuery.setExhibitionManageId(query.getExhibitionManageId());
        bondManageQuery.setServiceProviderId(query.getCompanyId());
        bondManageQuery.setSystemScene(MenuAffiliationEnum.P_COMPOSITE_COURT.getCode());
        bondManageQuery.setSpaceCode(query.getSpaceCode());
        bondManageQuery.setSpaceName(query.getSpaceName());
        bondManageQuery.setSiteCode(query.getSiteCode());
        bondManageQuery.setSiteName(query.getSiteName());
        List<BondManage> bondManageList = bondManageService.listBondManageForSettlement(bondManageQuery);
        if (bondManageList == null || bondManageList.isEmpty()) {
            return null;
        }
        ExhibitionSettlementVO exhibitionSettlementVO = BondManageService.calculateExhibitionSettlement(bondManageList);
        return exhibitionSettlementVO;
    }

    @Override
    public boolean checkSiteBondUnique(BondManageDTO bondManageDTO) {
        List<BondManage> list = listByExhibitorId(bondManageDTO.getExhibitorId());
        if (list == null || list.isEmpty()) {
            return true;
        }
        boolean isUnique = true;
        for (BondManage bondManage : list) {
            if (bondManage.getOrderStatus() != null && !BondManageOrderStatusEnum.CANCEL.eq(bondManage.getOrderStatus())) {
                isUnique = false;
                break;
            }
        }
        return isUnique;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public BondManage saveBondManage(BondManageDTO bondManageDTO) {
        log.info("saveBondManage - 保存保证金订单, bondManageDTO={}", bondManageDTO);
        return saveBondManage(bondManageDTO, true);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public BondManage saveBondManage(BondManageDTO bondManageDTO, Boolean isSyncPay) {
        log.info("saveBondManage - 保存保证金订单, bondManageDTO={}, isSyncPay={}", bondManageDTO, isSyncPay);
        // 订单状态初始化
        if (bondManageDTO.getOrderStatus() == null) {
            bondManageDTO.setOrderStatus(BondManageOrderStatusEnum.NOT_SUBMIT);
        }
        // 订单归属系统
        if (StrUtil.isBlank(bondManageDTO.getBelongSystem())) {
            bondManageDTO.setBelongSystem(MenuAffiliationEnum.P_COMPOSITE_PRODUCER.getCode());
        }
        // 保证金设置系统标识
        if (StrUtil.isBlank(bondManageDTO.getSystemScene())) {
            bondManageDTO.setSystemScene(MenuAffiliationEnum.P_COMPOSITE_COURT.getCode());
        }
        BondManage bondManage = bondManageService.saveBondManageInfo(bondManageDTO);
        if (isSyncPay) {
            // 生成支付系统交易订单
            saveTransactionOrder(bondManage.getId());
        }
        return bondManage;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public BondManage submitBondManage(BondManageDTO bondManageDTO) {
        return submitBondManage(bondManageDTO, true);
    }

    @Override
    public BondManage submitBondManage(BondManageDTO bondManageDTO, Boolean isSyncPay) {
        log.info("submitBondManage - 提交保证金订单, bondManageDTO={}, isSyncPay={}", bondManageDTO, isSyncPay);
        if (!checkSiteBondUnique(bondManageDTO)) {
            throw BizException.wrap(VenueServiceExceptionCode.BOND_MANAGE_HAS_EXIST.getCode(), "该展位已存在保证金订单");
        }
        // 处理保证金金额为0的情况
        BigDecimal bondAmount = bondManageDTO.getBondAmount() == null ? BigDecimal.ZERO : bondManageDTO.getBondAmount();
        if (StrUtil.isBlank(bondManageDTO.getPayStatus())) {
            if (BigDecimal.ZERO.compareTo(bondAmount) == 0) {
                bondManageDTO.setPayAmount(BigDecimal.ZERO);
                bondManageDTO.setPayStatus(BondManagePayStatusEnum.PAYED.getCode());
            }
        }
        BondManage bondManage = saveBondManage(bondManageDTO, isSyncPay);
        bondManage = submitBondManage(bondManage.getOrderNumber());
        return bondManage;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public BondManage submitBondManage(String orderNumber) {
        log.info("submitBondManage - 提交保证金订单, orderNumber={}", orderNumber);
        BondManage bondManage = bondManageService.getByOrderNumber(orderNumber);
        if (bondManage == null) {
            throw BizException.wrap(VenueServiceExceptionCode.BOND_MANAGE_NOT_EXIST.getCode(), "保证金订单不存在");
        }
        bondManage.setOrderTime(LocalDateTime.now());
        bondManage.setOrderStatus(BondManageOrderStatusEnum.SUBMIT);
        bondManageService.updateById(bondManage);
        // 更新违规扣款
        violationFineService.updateHomeBondOrderNumber(bondManage.getExhibitorId(), bondManage.getOrderNumber());
        return bondManage;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public BondManage submitForReportSpace(ReportSpaceAuditDTO reportSpaceAuditDTO) {
        // 生成保证金订单
        BondManageDTO bondManageDTO = toBondManageDTO(reportSpaceAuditDTO);
        BondManage bondManage = submitBondManage(bondManageDTO, false);
        // 更新展商展位的保证金
        ExhibitorDTO exhibitorDTO = new ExhibitorDTO();
        exhibitorDTO.setId(reportSpaceAuditDTO.getExhibitorId());
        exhibitorDTO.setShouldEarnestMoney(reportSpaceAuditDTO.getEarnestMoney());
        venueManageExhibitorService.updateShouldEarnestMoney(exhibitorDTO);
        // 如果应付保证金金额为0，则更新展商展位的保证金缴纳状态为已支付
        if (BondManagePayStatusEnum.PAYED.getCode().equals(bondManage.getPayStatus())) {
            venueManageExhibitorService.updateEarnestMoneyByBondManage(bondManage);
        }
        return bondManage;
    }

    @Override
    public int canDeductBond(Long exhibitorId, BigDecimal shouldDeductAmount) {
        log.info("canDeductBond - 是否可扣除保证金, exhibitorId={}, shouldDeductAmount={}", exhibitorId, shouldDeductAmount);
        // 没有保证金，则不可扣除
        List<BondManage> bondManageList = listByExhibitorId(exhibitorId);
        if (bondManageList == null || bondManageList.isEmpty()) {
            return BusinessConstant.NO;
        }
        BondManage bondManage = bondManageList.get(0);
        // 保证金已取消，不可扣除
        if (BondManageOrderStatusEnum.CANCEL.eq(bondManage.getOrderStatus())) {
            return BusinessConstant.NO;
        }
//        // 保证金未支付完成，不可扣除
//        if (!BondManagePayStatusEnum.PAYED.getCode().equals(bondManage.getPayStatus())) {
//            return BusinessConstant.NO;
//        }
//        // 保证金支付金额未0，不可扣除
//        if (bondManage.getPayAmount() == null || BigDecimal.ZERO.equals(bondManage.getPayAmount())) {
//            return BusinessConstant.NO;
//        }
//        // 保证金可扣除金额不足，不可扣除
//        BigDecimal bondRefundableAmount = bondManage.getRefundableAmount() == null ? BigDecimal.ZERO : bondManage.getRefundableAmount();
//        if (shouldDeductAmount.compareTo(bondRefundableAmount) > 0) {
//            return BusinessConstant.NO;
//        }
        return BusinessConstant.YES;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public BondManage deductBondAmount(Long exhibitorId, BigDecimal shouldDeductAmount) {
        log.info("deductBondAmount - 扣除保证金, exhibitorId={}, shouldDeductAmount={}", exhibitorId, shouldDeductAmount);
        List<BondManage> bondManageList = listByExhibitorId(exhibitorId);
        if (bondManageList == null || bondManageList.isEmpty()) {
            return null;
        }
        // 更新可退款金额
        BondManage bondManage = bondManageList.get(0);
        return bondManageService.deductBondAmount(bondManage, shouldDeductAmount);
    }

    @Override
    public BondManage deductBondAmount(String orderNumber, BigDecimal shouldDeductAmount) {
        return bondManageService.deductBondAmount(orderNumber, shouldDeductAmount);
    }

    @Override
    public boolean refundBondManage(Long exhibitorId, BigDecimal shouldRefundAmount) {
        List<BondManage> bondManageList = listByExhibitorId(exhibitorId);
        if (bondManageList == null || bondManageList.isEmpty()) {
            throw BizException.wrap(VenueServiceExceptionCode.BOND_MANAGE_NOT_EXIST);
        }
        BondManage bondManage = bondManageList.get(0);
        return bondManageService.refundBondManage(bondManage, shouldRefundAmount);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean refundBondManage(String orderNumber, BigDecimal shouldRefundAmount) {
        return bondManageService.refundBondManage(orderNumber, shouldRefundAmount);
    }

    @Override
    public boolean cancelBondManage(String orderNumber) {
        log.info("cancelBondManage - 取消保证金订单, orderNumber={}", orderNumber);
        BondManage bondManage = bondManageService.getByOrderNumber(orderNumber);
        if (bondManage == null) {
            throw BizException.wrap(VenueServiceExceptionCode.BOND_MANAGE_NOT_EXIST.getCode(), "保证金订单不存在");
        }
        // 发起退款申请
        if (BondManagePayStatusEnum.PAYED.getCode().equals(bondManage.getPayStatus()) || BondManagePayStatusEnum.PARTPAY.getCode().equals(bondManage.getPayStatus())) {
            BondManageDTO bondManageDTO = BeanUtil.toBean(bondManage, BondManageDTO.class);
            applyBondManageRefund(bondManageDTO);
            // 如果订单已经支付，支付系统订单先不能取消，要等退款结束之后再取消，取消支付系统交易订单
        } else {
             venueManageTransactionOrderService.cancel(orderNumber);
        }
        // 更新退款状态为已取消
        updateBondManageCancel(orderNumber);
        return true;
    }

    @Override
    public BondManage updateBondManageCancel(String orderNumber) {
        log.info("updateBondManageCancel - 更新保证金状态为已取消, orderNumber={}", orderNumber);
        return transactionTemplate.execute(status -> {
            BondManage bondManage = bondManageService.getByOrderNumber(orderNumber);
            if (bondManage == null) {
                throw BizException.wrap(VenueServiceExceptionCode.BOND_MANAGE_NOT_EXIST.getCode(), "保证金订单不存在");
            }
            bondManage.setOrderStatus(BondManageOrderStatusEnum.CANCEL);
            bondManageService.updateById(bondManage);
            // 取消违规扣款
            violationFineService.cancelByBondOrderNumber(orderNumber);
            return bondManage;
        });
    }

    @Override
    public boolean saveTransactionOrder(Long id) {
        BondManage bondManage = bondManageService.getById(id);
        if (bondManage == null) {
            throw BizException.wrap(VenueServiceExceptionCode.BOND_MANAGE_NOT_EXIST);
        }
        BondManageDTO bondManageDTO = completeForTransactionOrder(bondManage);
        try {
            bondManageService.saveTransactionOrder(bondManageDTO);
        } catch (BizException e) {
            // 发送配置收款账号的站内信
            if (e.getCode() == VenueServiceExceptionCode.PAYEE_NOT_CONFIG.getCode()) {
                asyncSendConfigPayeeInnerMsg(bondManageDTO);
                return true;
            }
            throw e;
        }
        return true;
    }

    @Override
    public BondManageDTO completeForTransactionOrder(BondManage bondManage) {
        BondManageDTO bondManageDTO = BeanUtil.toBean(bondManage, BondManageDTO.class);
        ServiceProviderVO home = exhibitionServiceProviderService.getHomeByExhibitionIdAndSpaceCode(bondManageDTO.getExhibitionManageId(), bondManageDTO.getSpaceCode());
        if (home == null) {
            throw BizException.wrap(VenueServiceExceptionCode.PAYEE_NOT_EXIST.getCode(), "未找到负责该展厅的主场，无法生成保证金订单");
        }
        bondManageDTO.setPayee(home.getCompanyName());
        return bondManageDTO;
    }

    @Override
    public boolean applyBondManageRefund(BondManageDTO bondManageDTO) {
        log.info("applyBondManageRefund - 发起退款，bondManageDTO={}", bondManageDTO);
        bondManageService.applyBondManageRefund(bondManageDTO);
        return true;
    }

    @Override
    public boolean sendConfigPayeeInnerMsg(BondManageDTO bondManageDTO) {
        log.info("sendConfigPayeeInnerMsg - 发送配置收款商户的站内信, bondManageDTO={}", bondManageDTO);
        R<Boolean> result = homeHomeBondManageFeign.sendConfigPayeeInnerMsg(bondManageDTO);
        log.info("sendConfigPayeeInnerMsg - 发送配置收款商户的站内信完成, result={}", result);
        if (!result.getIsSuccess()) {
            throw BizException.wrap(result.getCode(), result.getMsg());
        }
        return result.getData();
    }

    @Override
    public void asyncSendConfigPayeeInnerMsg(BondManageDTO bondManageDTO) {
        log.info("asyncSendConfigPayeeInnerMsg - 异步发送配置收款商户的站内信, bondManageDTO={}", bondManageDTO);
        threadPoolTaskExecutor.execute(() -> sendConfigPayeeInnerMsg(bondManageDTO));
    }

    private BondManageDTO toBondManageDTO(ReportSpaceAuditDTO reportSpaceAuditDTO) {
        BondManageDTO bondManageDTO = new BondManageDTO();
        bondManageDTO.setExhibitionManageId(reportSpaceAuditDTO.getExhibitionId());
        bondManageDTO.setExhibitionManageName(reportSpaceAuditDTO.getExhibitionName());
        //  封装订单企业信息
        ServiceProvider serviceProvider = serviceProviderService.getById(reportSpaceAuditDTO.getBuilderId());
        if (serviceProvider == null) {
            throw BizException.wrap(VenueServiceExceptionCode.SERVICE_PROVIDER_NOT_EXIST);
        }
        bondManageDTO.setServiceProviderId(serviceProvider.getId());
        bondManageDTO.setServiceProviderCode(serviceProvider.getCompanyCode());
        bondManageDTO.setServiceProviderName(serviceProvider.getCompanyName());
        bondManageDTO.setServiceProviderType(serviceProvider.getServiceProviderType());
        bondManageDTO.setContact(serviceProvider.getPrincipalName());
        bondManageDTO.setPhone(serviceProvider.getPrincipalMobile());
        bondManageDTO.setExhibitorId(reportSpaceAuditDTO.getExhibitorId());
        bondManageDTO.setSpaceCode(reportSpaceAuditDTO.getSpaceCode());
        bondManageDTO.setSpaceName(reportSpaceAuditDTO.getSpaceName());
        bondManageDTO.setSiteCode(reportSpaceAuditDTO.getSiteCode());
        bondManageDTO.setSiteName(reportSpaceAuditDTO.getSiteName());
        bondManageDTO.setSiteArea(reportSpaceAuditDTO.getSiteArea());
        bondManageDTO.setBondAmount(reportSpaceAuditDTO.getEarnestMoney());
        return bondManageDTO;
    }
}